<?php
	
	namespace Aspic\Controller;
	use Aspic as A;
	
	/**
	* Base controller class. All controllers of the application should extends it
	* 
	* 
	* @Author Mallory Dessaintes <mdessaintes@gmail.com>
	* @version 1.0
	*/
	abstract class AbstractController extends AbstractControllerBase {
		const CONTROLLERS_NS = 'Controllers';
		// const MODELS_NS = 'Models';
//		const FORMS_NS = 'Forms';
		
		const CONTROLLERS_CLASS_NAMES_SUFFIX = 'Controller';
//		const MODELS_CLASS_NAMES_SUFFIX = 'Model';
//		const FORMS_CLASS_NAMES_SUFFIX = 'Form';
		
		const ERROR_CONTROLLER_NAME = 'error';
		
		const ACTIONS_METHODS_NAMES_SUFFIX = 'Action';
		
		/**
		 * @var array List of all actions that have been loaded
		 */
		protected $_loadedActions;


		protected $_moduleInst;
		
		public function __construct($moduleInst) {
			parent::__construct($moduleInst->getViewObject());
			
			$this->_moduleInst = $moduleInst;
			$this->_loadedActions = array();
		}
		
		/**
		* Get the current action's name
		*/
		protected function getCurrentActionName() {
			return $this->_loadedActions[count($this->_loadedActions) - 1];
		}
		
		/**
		* Load an action of the current controller
		*/
		protected function loadAction($actionName = null, array $params = array(), array $specialParams = array()) {
			$defaultSpecialParams = array(
				'loadView' => true,
				'doControllerInit' => true, 
				'doControllerEnd' => true, 
				// If true, return the template output instead of printing it
				'returnTemplateOutput' => false, 
			);
			
			$this->_specialParams = array_merge($defaultSpecialParams, $specialParams);
			
			$this->_loadedActions[] = $actionName;
			$firstActionCallInController = count($this->_loadedActions) == 1;
			
			$actionMethodName = static::getActionMethodName($actionName);
			
			// Do not call init when loadAction is called multiple times
			if ($this->getParam('doControllerInit') AND $firstActionCallInController == true) {
				$tmp = $this->{static::INIT_METHOD_NAME}();
			}
			
			$viewRelativePath = null;
			
			if($this->_isSuspended) {
				$viewRelativePath = $tmp;
			}
			else {
				if(method_exists($this, $actionMethodName)) {
					$viewRelativePath = call_user_func_array(array($this, $actionMethodName), $params);
				}
				else {
					throw new ActionNotFoundException($actionName, $actionMethodName);
				}
			}
			
			if ($this->getParam('doControllerEnd') AND $firstActionCallInController == true) {
				$this->{static::END_METHOD_NAME}();
			}
			
			if($this->getParam('loadView') AND !is_null($viewRelativePath)) {
				if ($this->getParam('returnTemplateOutput')) {
					$this->loadViewRelativePath($viewRelativePath, true);
				}
				else {
					$this->loadViewRelativePath($viewRelativePath);
				}
			}
		}
		
		protected function actionExists($actionName) {
			$actionMethodName = static::getActionMethodName($actionName);
			
			return method_exists($this, $actionMethodName);
		}
		
		/**
		* Generate a default template name (here controller/action.phtml) from given data 
		* or from current controller / action if omitted
		*/
		protected function getAutoTemplateName($action = null, $controller = null, $ext = 'phtml') {
			if($controller == null) {
				$controller = $this->getCurrentControllerName();
			}
			
			if($action == null) {
				$action = $this->getCurrentActionName();
			}
			
			$name = A\UString::joinPath($controller, $action).'.'.$ext;
			
			return $name;
		}
		
		protected function getCurrentControllerName() {
			return strtolower(substr(
				A\Util::getClassName($this),
				0,
				strpos(
					A\Util::getClassName($this), 
					static::CONTROLLERS_CLASS_NAMES_SUFFIX
				)
			));
		}
		
		protected function getCurrentModuleName() {
			return $this->_moduleInst->getCurrentModuleName();
		}
		
		protected function getCurrentModulePath() {
			return $this->_moduleInst->getCurrentModulePath();
		}
		
		protected function getCurrentModule() {
			return $this->_moduleInst;
		}
		
		/**
		* Get action method name from action name
		*/
		protected static function getActionMethodName($actionName) {
			$method = $actionName.static::ACTIONS_METHODS_NAMES_SUFFIX;
			
			return $method;
		}
		
		/**
		* Get controller class name from controller name
		* => Make the link between given name (by module class) and controller class
		*/
		protected static function getControllerClassName($controllerName) {
			$class = ucfirst($controllerName).static::CONTROLLERS_CLASS_NAMES_SUFFIX;
			
			return $class;
		}
		
		protected static function controllerExists($controllerName, $namespace) {
			$classPath = static::getControllerClassPath($controllerName, $namespace);
			
			return class_exists($classPath);
		}
		
		public static function loadController($controller, $action, array $params, array $specialParams, $moduleInst) {
			if(!static::controllerExists($controller, $moduleInst->getCurrentModuleNamespace())) {
				throw new ControllerNotFoundException($controller, static::getControllerClassPath($controller, $moduleInst->getCurrentModuleNamespace()));
			}
			
			$controllerClassPath = static::getControllerClassPath($controller, $moduleInst->getCurrentModuleNamespace());
			
			if(static::controllerExists($controller, $moduleInst->getCurrentModuleNamespace())) {
				$controllerInst = new $controllerClassPath($moduleInst);
				
				$controllerInst->loadAction($action, $params);
			}
			else {
				throw new ControllerNotFoundException($realController, $controllerClassPath);
			}
		}
		
		public static function getControllersNamespace($namespace) {
			$controllerNs = A\UString::join('\\', $namespace, static::CONTROLLERS_NS);
			
			return $controllerNs;
		}
		
		public static function getControllerClassPath($controllerName, $namespace) {
			$controllerClassPath = A\UString::join('\\', static::getControllersNamespace($namespace), static::getControllerClassName($controllerName));
			
			return $controllerClassPath;
		}
		
	}
	
?>